﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Framework.Core
{
    public static class TypeExtension
    {
        public static bool HasCustomAttribute<T>(this Type type)
            where T : Attribute
        {
            return type.GetCustomAttributes<T>(true).Any();
        }

        public static IEnumerable<T> GetCustomAttributes<T>(this Type type, bool inherit)
            where T : Attribute
        {
            return GetCustomAttributes(type, typeof (T), inherit).Select(arg => (T) arg);
        }

        private static IEnumerable<object> GetCustomAttributes(Type type, Type attributeType, bool inherit)
        {
            if (!inherit)
            {
                return type.GetCustomAttributes(attributeType, false);
            }

            var attributes = new List<object>();
            var baseType = type;

            do
            {
                baseType.GetCustomAttributes(attributeType, true).Apply(attributes.Add);
                baseType = baseType.BaseType;
            } while (baseType != null);

            foreach (var interfaceType in type.GetInterfaces())
            {
                GetCustomAttributes(interfaceType, attributeType, true).Apply(attributes.Add);
            }

            return attributes;
        }

        private static void Apply<T>(this IEnumerable<T> enumerable, Action<T> function)
        {
            foreach (var item in enumerable)
            {
                function.Invoke(item);
            }
        }

        public static bool IsAssignableFromGenericType(this Type genericType, Type givenType)
        {
            var types = givenType.GetInterfaces().ToList();
            types.Add(givenType.BaseType);

            return types.Any(t => IsAssignableFromGenericTypeFirstNode(genericType, t));
        }

        private static bool IsAssignableFromGenericTypeFirstNode(Type genericType, Type givenType)
        {
            return givenType != null && givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType;
        }
    }
}